{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Problem: Train an Autoencoder for Anomaly Detection\n",
    "\n",
    "### Problem Statement\n",
    "You are tasked with implementing an **autoencoder** model for anomaly detection. The model will be trained on the **MNIST dataset**, and anomalies will be detected based on the reconstruction error. The autoencoder consists of an encoder to compress the input and a decoder to reconstruct the image. The difference between the original image and the reconstructed image will be used to detect anomalies.\n",
    "\n",
    "### Requirements\n",
    "1. **Define the Autoencoder Architecture**:\n",
    "   - **Encoder**:\n",
    "     - Implement a series of convolutional layers followed by max-pooling layers.\n",
    "     - The encoder should progressively reduce the spatial dimensions of the input image, capturing the most important features.\n",
    "   - **Decoder**:\n",
    "     - Implement a series of transposed convolutional layers (also known as deconvolutional layers) to upsample the compressed representation back to the original image size.\n",
    "     - Use a **Sigmoid activation** function in the final layer to ensure that the output pixel values are between 0 and 1.\n",
    "\n",
    "2. **Forward Pass**:\n",
    "   - Implement the forward method where the input image is passed through the encoder to obtain a compressed representation, followed by passing it through the decoder to reconstruct the image.\n",
    "\n",
    "### Constraints\n",
    "- The autoencoder should work on the MNIST dataset, which consists of 28x28 grayscale images.\n",
    "- Ensure that the output of the decoder matches the original image size.\n",
    "- Use **Sigmoid activation** in the final layer to constrain the output pixel values between 0 and 1.\n",
    "\n",
    "<details>\n",
    "  <summary>💡 Hint</summary>\n",
    "  Focus on the encoder to downsample the input and the decoder to upsample and reconstruct the image.\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load MNIST dataset\n",
    "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n",
    "\n",
    "train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)\n",
    "train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)\n",
    "\n",
    "test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)\n",
    "test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define an Autoencoder model\n",
    "# TODO: Implement the autoencoder architecture\n",
    "class Autoencoder(nn.Module):\n",
    "    def __init__(self):\n",
    "        # init\n",
    "        ...\n",
    "        # Encoder\n",
    "        self.encoder = ...\n",
    "        # Decoder\n",
    "        self.decoder = ...\n",
    "\n",
    "    def forward(self, x):\n",
    "        ...\n",
    "\n",
    "# Initialize the model, loss function, and optimizer\n",
    "model = Autoencoder()\n",
    "criterion = nn.MSELoss()\n",
    "optimizer = optim.Adam(model.parameters(), lr=0.001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/10], Loss: 0.9243\n",
      "Epoch [2/10], Loss: 0.8939\n",
      "Epoch [3/10], Loss: 0.9047\n",
      "Epoch [4/10], Loss: 0.8792\n",
      "Epoch [5/10], Loss: 0.8849\n",
      "Epoch [6/10], Loss: 0.8775\n",
      "Epoch [7/10], Loss: 0.8920\n",
      "Epoch [8/10], Loss: 0.8684\n",
      "Epoch [9/10], Loss: 0.8748\n",
      "Epoch [10/10], Loss: 0.8688\n"
     ]
    }
   ],
   "source": [
    "# Training loop\n",
    "epochs = 10\n",
    "for epoch in range(epochs):\n",
    "    for images, _ in train_loader:\n",
    "        # Forward pass\n",
    "        reconstructed = model(images)\n",
    "        loss = criterion(reconstructed, images)\n",
    "\n",
    "        # Backward pass and optimization\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "    print(f\"Epoch [{epoch + 1}/{epochs}], Loss: {loss.item():.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Detect anomalies using reconstruction error\n",
    "threshold = 0.1  # Define a threshold for anomaly detection\n",
    "model.eval()\n",
    "anomalies = []\n",
    "with torch.no_grad():\n",
    "    for images, _ in test_loader:\n",
    "        reconstructed = model(images)\n",
    "        loss = criterion(reconstructed, images)\n",
    "        \n",
    "        # If reconstruction error exceeds the threshold, mark it as an anomaly\n",
    "        if loss.item() > threshold:\n",
    "            anomalies.append(images)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Anomaly image shape: torch.Size([28, 28])\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaAAAAGdCAYAAABU0qcqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAaqElEQVR4nO3df2xV9f3H8VeL9ILaXiylvb2jQEEFwy8ng9rwYygNtC4GtEtA/QMWAoFdzLDzx7qIKFvSjSWOuCD+s8BMxF+JQCRLMym2hNliqDDCph3tugGBFsVxbylSGP18/yDer1cKeMq9ffdeno/kJPTe8+l9ezzhyWlvT9Occ04AAPSxdOsBAAA3JwIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBM3GI9wLd1d3frxIkTyszMVFpamvU4AACPnHPq6OhQMBhUevrVr3P6XYBOnDihgoIC6zEAADfo2LFjGj58+FWf73dfgsvMzLQeAQAQB9f7+zxhAdq4caNGjRqlQYMGqaioSB9//PF3WseX3QAgNVzv7/OEBOjtt99WRUWF1q5dq08++USTJ0/WvHnzdOrUqUS8HAAgGbkEmDZtmguFQtGPL1265ILBoKuqqrru2nA47CSxsbGxsSX5Fg6Hr/n3fdyvgC5cuKDGxkaVlJREH0tPT1dJSYnq6+uv2L+rq0uRSCRmAwCkvrgH6IsvvtClS5eUl5cX83heXp7a2tqu2L+qqkp+vz+68Q44ALg5mL8LrrKyUuFwOLodO3bMeiQAQB+I+88B5eTkaMCAAWpvb495vL29XYFA4Ir9fT6ffD5fvMcAAPRzcb8CysjI0JQpU1RTUxN9rLu7WzU1NSouLo73ywEAklRC7oRQUVGhxYsX6wc/+IGmTZumDRs2qLOzUz/5yU8S8XIAgCSUkAAtXLhQn3/+uV544QW1tbXp3nvvVXV19RVvTAAA3LzSnHPOeohvikQi8vv91mMAAG5QOBxWVlbWVZ83fxccAODmRIAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATMQ9QC+++KLS0tJitnHjxsX7ZQAASe6WRHzS8ePHa9euXf//Irck5GUAAEksIWW45ZZbFAgEEvGpAQApIiHfAzpy5IiCwaBGjx6tJ554QkePHr3qvl1dXYpEIjEbACD1xT1ARUVF2rJli6qrq7Vp0ya1trZq5syZ6ujo6HH/qqoq+f3+6FZQUBDvkQAA/VCac84l8gXOnDmjkSNH6uWXX9bSpUuveL6rq0tdXV3RjyORCBECgBQQDoeVlZV11ecT/u6AIUOG6O6771Zzc3OPz/t8Pvl8vkSPAQDoZxL+c0Bnz55VS0uL8vPzE/1SAIAkEvcAPf3006qrq9O///1vffTRR3rkkUc0YMAAPfbYY/F+KQBAEov7l+COHz+uxx57TKdPn9awYcM0Y8YMNTQ0aNiwYfF+KQBAEkv4mxC8ikQi8vv91mMAAG7Q9d6EwL3gAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATCf+FdOhbP/7xjz2vWbZsWa9e68SJE57XnD9/3vOaN954w/OatrY2z2skXfUXJwKIP66AAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYCLNOeesh/imSCQiv99vPUbS+te//uV5zahRo+I/iLGOjo5erfv73/8e50kQb8ePH/e8Zv369b16rf379/dqHS4Lh8PKysq66vNcAQEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJm6xHgDxtWzZMs9rJk2a1KvX+vTTTz2vueeeezyvue+++zyvmT17tuc1knT//fd7XnPs2DHPawoKCjyv6Uv/+9//PK/5/PPPPa/Jz8/3vKY3jh492qt13Iw0sbgCAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMcDPSFFNTU9Mna3qrurq6T17njjvu6NW6e++91/OaxsZGz2umTp3qeU1fOn/+vOc1//znPz2v6c0NbbOzsz2vaWlp8bwGiccVEADABAECAJjwHKA9e/bo4YcfVjAYVFpamrZv3x7zvHNOL7zwgvLz8zV48GCVlJToyJEj8ZoXAJAiPAeos7NTkydP1saNG3t8fv369XrllVf02muvad++fbrttts0b968Xn1NGQCQujy/CaGsrExlZWU9Puec04YNG/T8889r/vz5kqTXX39deXl52r59uxYtWnRj0wIAUkZcvwfU2tqqtrY2lZSURB/z+/0qKipSfX19j2u6uroUiURiNgBA6otrgNra2iRJeXl5MY/n5eVFn/u2qqoq+f3+6FZQUBDPkQAA/ZT5u+AqKysVDoej27Fjx6xHAgD0gbgGKBAISJLa29tjHm9vb48+920+n09ZWVkxGwAg9cU1QIWFhQoEAjE/WR+JRLRv3z4VFxfH86UAAEnO87vgzp49q+bm5ujHra2tOnjwoLKzszVixAitXr1av/71r3XXXXepsLBQa9asUTAY1IIFC+I5NwAgyXkO0P79+/XAAw9EP66oqJAkLV68WFu2bNGzzz6rzs5OLV++XGfOnNGMGTNUXV2tQYMGxW9qAEDSS3POOeshvikSicjv91uPAcCj8vJyz2veeecdz2sOHz7sec03/9HsxZdfftmrdbgsHA5f8/v65u+CAwDcnAgQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGDC869jAJD6cnNzPa959dVXPa9JT/f+b+B169Z5XsNdrfsnroAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABPcjBTAFUKhkOc1w4YN87zmv//9r+c1TU1Nntegf+IKCABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwwc1IgRQ2ffr0Xq37xS9+EedJerZgwQLPaw4fPhz/QWCCKyAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQ3IwVS2EMPPdSrdQMHDvS8pqamxvOa+vp6z2uQOrgCAgCYIEAAABOeA7Rnzx49/PDDCgaDSktL0/bt22OeX7JkidLS0mK20tLSeM0LAEgRngPU2dmpyZMna+PGjVfdp7S0VCdPnoxub7755g0NCQBIPZ7fhFBWVqaysrJr7uPz+RQIBHo9FAAg9SXke0C1tbXKzc3V2LFjtXLlSp0+ffqq+3Z1dSkSicRsAIDUF/cAlZaW6vXXX1dNTY1++9vfqq6uTmVlZbp06VKP+1dVVcnv90e3goKCeI8EAOiH4v5zQIsWLYr+eeLEiZo0aZLGjBmj2tpazZkz54r9KysrVVFREf04EokQIQC4CST8bdijR49WTk6Ompube3ze5/MpKysrZgMApL6EB+j48eM6ffq08vPzE/1SAIAk4vlLcGfPno25mmltbdXBgweVnZ2t7OxsvfTSSyovL1cgEFBLS4ueffZZ3XnnnZo3b15cBwcAJDfPAdq/f78eeOCB6Mdff/9m8eLF2rRpkw4dOqQ//elPOnPmjILBoObOnatf/epX8vl88ZsaAJD00pxzznqIb4pEIvL7/dZjAP3O4MGDPa/Zu3dvr15r/Pjxntc8+OCDntd89NFHntcgeYTD4Wt+X597wQEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMBE3H8lN4DEeOaZZzyv+f73v9+r16qurva8hjtbwyuugAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAE9yMFDDwox/9yPOaNWvWeF4TiUQ8r5GkdevW9Wod4AVXQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACW5GCtygoUOHel7zyiuveF4zYMAAz2v+/Oc/e14jSQ0NDb1aB3jBFRAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIKbkQLf0JsbflZXV3teU1hY6HlNS0uL5zVr1qzxvAboK1wBAQBMECAAgAlPAaqqqtLUqVOVmZmp3NxcLViwQE1NTTH7nD9/XqFQSEOHDtXtt9+u8vJytbe3x3VoAEDy8xSguro6hUIhNTQ06IMPPtDFixc1d+5cdXZ2Rvd56qmn9P777+vdd99VXV2dTpw4oUcffTTugwMAkpunNyF8+5utW7ZsUW5urhobGzVr1iyFw2H98Y9/1NatW/Xggw9KkjZv3qx77rlHDQ0Nuv/+++M3OQAgqd3Q94DC4bAkKTs7W5LU2NioixcvqqSkJLrPuHHjNGLECNXX1/f4Obq6uhSJRGI2AEDq63WAuru7tXr1ak2fPl0TJkyQJLW1tSkjI0NDhgyJ2TcvL09tbW09fp6qqir5/f7oVlBQ0NuRAABJpNcBCoVCOnz4sN56660bGqCyslLhcDi6HTt27IY+HwAgOfTqB1FXrVqlnTt3as+ePRo+fHj08UAgoAsXLujMmTMxV0Ht7e0KBAI9fi6fzyefz9ebMQAASczTFZBzTqtWrdK2bdu0e/fuK36ae8qUKRo4cKBqamqijzU1Neno0aMqLi6Oz8QAgJTg6QooFApp69at2rFjhzIzM6Pf1/H7/Ro8eLD8fr+WLl2qiooKZWdnKysrS08++aSKi4t5BxwAIIanAG3atEmSNHv27JjHN2/erCVLlkiSfv/73ys9PV3l5eXq6urSvHnz9Oqrr8ZlWABA6khzzjnrIb4pEonI7/dbj4Gb1N133+15zWeffZaASa40f/58z2vef//9BEwCfDfhcFhZWVlXfZ57wQEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMBEr34jKtDfjRw5slfr/vKXv8R5kp4988wzntfs3LkzAZMAdrgCAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMcDNSpKTly5f3at2IESPiPEnP6urqPK9xziVgEsAOV0AAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAluRop+b8aMGZ7XPPnkkwmYBEA8cQUEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJjgZqTo92bOnOl5ze23356ASXrW0tLiec3Zs2cTMAmQXLgCAgCYIEAAABOeAlRVVaWpU6cqMzNTubm5WrBggZqammL2mT17ttLS0mK2FStWxHVoAEDy8xSguro6hUIhNTQ06IMPPtDFixc1d+5cdXZ2xuy3bNkynTx5MrqtX78+rkMDAJKfpzchVFdXx3y8ZcsW5ebmqrGxUbNmzYo+fuuttyoQCMRnQgBASrqh7wGFw2FJUnZ2dszjb7zxhnJycjRhwgRVVlbq3LlzV/0cXV1dikQiMRsAIPX1+m3Y3d3dWr16taZPn64JEyZEH3/88cc1cuRIBYNBHTp0SM8995yampr03nvv9fh5qqqq9NJLL/V2DABAkup1gEKhkA4fPqy9e/fGPL58+fLonydOnKj8/HzNmTNHLS0tGjNmzBWfp7KyUhUVFdGPI5GICgoKejsWACBJ9CpAq1at0s6dO7Vnzx4NHz78mvsWFRVJkpqbm3sMkM/nk8/n680YAIAk5ilAzjk9+eST2rZtm2pra1VYWHjdNQcPHpQk5efn92pAAEBq8hSgUCikrVu3aseOHcrMzFRbW5skye/3a/DgwWppadHWrVv10EMPaejQoTp06JCeeuopzZo1S5MmTUrIfwAAIDl5CtCmTZskXf5h02/avHmzlixZooyMDO3atUsbNmxQZ2enCgoKVF5erueffz5uAwMAUoPnL8FdS0FBgerq6m5oIADAzYG7YQPf8Le//c3zmjlz5nhe8+WXX3peA6QabkYKADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJhIc9e7xXUfi0Qi8vv91mMAAG5QOBxWVlbWVZ/nCggAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAICJfhegfnZrOgBAL13v7/N+F6COjg7rEQAAcXC9v8/73d2wu7u7deLECWVmZiotLS3muUgkooKCAh07duyad1hNdRyHyzgOl3EcLuM4XNYfjoNzTh0dHQoGg0pPv/p1zi19ONN3kp6eruHDh19zn6ysrJv6BPsax+EyjsNlHIfLOA6XWR+H7/Jrdfrdl+AAADcHAgQAMJFUAfL5fFq7dq18Pp/1KKY4DpdxHC7jOFzGcbgsmY5Dv3sTAgDg5pBUV0AAgNRBgAAAJggQAMAEAQIAmEiaAG3cuFGjRo3SoEGDVFRUpI8//th6pD734osvKi0tLWYbN26c9VgJt2fPHj388MMKBoNKS0vT9u3bY553zumFF15Qfn6+Bg8erJKSEh05csRm2AS63nFYsmTJFedHaWmpzbAJUlVVpalTpyozM1O5ublasGCBmpqaYvY5f/68QqGQhg4dqttvv13l5eVqb283mjgxvstxmD179hXnw4oVK4wm7llSBOjtt99WRUWF1q5dq08++USTJ0/WvHnzdOrUKevR+tz48eN18uTJ6LZ3717rkRKus7NTkydP1saNG3t8fv369XrllVf02muvad++fbrttts0b948nT9/vo8nTazrHQdJKi0tjTk/3nzzzT6cMPHq6uoUCoXU0NCgDz74QBcvXtTcuXPV2dkZ3eepp57S+++/r3fffVd1dXU6ceKEHn30UcOp4++7HAdJWrZsWcz5sH79eqOJr8IlgWnTprlQKBT9+NKlSy4YDLqqqirDqfre2rVr3eTJk63HMCXJbdu2Lfpxd3e3CwQC7ne/+130sTNnzjifz+fefPNNgwn7xrePg3POLV682M2fP99kHiunTp1yklxdXZ1z7vL/+4EDB7p33303us+nn37qJLn6+nqrMRPu28fBOed++MMfup/97Gd2Q30H/f4K6MKFC2psbFRJSUn0sfT0dJWUlKi+vt5wMhtHjhxRMBjU6NGj9cQTT+jo0aPWI5lqbW1VW1tbzPnh9/tVVFR0U54ftbW1ys3N1dixY7Vy5UqdPn3aeqSECofDkqTs7GxJUmNjoy5evBhzPowbN04jRoxI6fPh28fha2+88YZycnI0YcIEVVZW6ty5cxbjXVW/uxnpt33xxRe6dOmS8vLyYh7Py8vTZ599ZjSVjaKiIm3ZskVjx47VyZMn9dJLL2nmzJk6fPiwMjMzrccz0dbWJkk9nh9fP3ezKC0t1aOPPqrCwkK1tLTol7/8pcrKylRfX68BAwZYjxd33d3dWr16taZPn64JEyZIunw+ZGRkaMiQITH7pvL50NNxkKTHH39cI0eOVDAY1KFDh/Tcc8+pqalJ7733nuG0sfp9gPD/ysrKon+eNGmSioqKNHLkSL3zzjtaunSp4WToDxYtWhT988SJEzVp0iSNGTNGtbW1mjNnjuFkiREKhXT48OGb4vug13K147B8+fLonydOnKj8/HzNmTNHLS0tGjNmTF+P2aN+/yW4nJwcDRgw4Ip3sbS3tysQCBhN1T8MGTJEd999t5qbm61HMfP1OcD5caXRo0crJycnJc+PVatWaefOnfrwww9jfn1LIBDQhQsXdObMmZj9U/V8uNpx6ElRUZEk9avzod8HKCMjQ1OmTFFNTU30se7ubtXU1Ki4uNhwMntnz55VS0uL8vPzrUcxU1hYqEAgEHN+RCIR7du376Y/P44fP67Tp0+n1PnhnNOqVau0bds27d69W4WFhTHPT5kyRQMHDow5H5qamnT06NGUOh+udxx6cvDgQUnqX+eD9bsgvou33nrL+Xw+t2XLFvePf/zDLV++3A0ZMsS1tbVZj9anfv7zn7va2lrX2trq/vrXv7qSkhKXk5PjTp06ZT1aQnV0dLgDBw64AwcOOEnu5ZdfdgcOHHD/+c9/nHPO/eY3v3FDhgxxO3bscIcOHXLz5893hYWF7quvvjKePL6udRw6Ojrc008/7err611ra6vbtWuXu++++9xdd93lzp8/bz163KxcudL5/X5XW1vrTp48Gd3OnTsX3WfFihVuxIgRbvfu3W7//v2uuLjYFRcXG04df9c7Ds3NzW7dunVu//79rrW11e3YscONHj3azZo1y3jyWEkRIOec+8Mf/uBGjBjhMjIy3LRp01xDQ4P1SH1u4cKFLj8/32VkZLjvfe97buHCha65udl6rIT78MMPnaQrtsWLFzvnLr8Ve82aNS4vL8/5fD43Z84c19TUZDt0AlzrOJw7d87NnTvXDRs2zA0cONCNHDnSLVu2LOX+kdbTf78kt3nz5ug+X331lfvpT3/q7rjjDnfrrbe6Rx55xJ08edJu6AS43nE4evSomzVrlsvOznY+n8/deeed7plnnnHhcNh28G/h1zEAAEz0++8BAQBSEwECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABg4v8AjVqFRqQZEfIAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualize anomalies\n",
    "if anomalies:\n",
    "    # Select the first anomaly and remove the channel dimension for visualization\n",
    "    anomaly_image = anomalies[0][0].squeeze()  # Remove the channel dimension (1)\n",
    "    print(f\"Anomaly image shape: {anomaly_image.shape}\")  # Optional: Check the shape of the image\n",
    "    plt.imshow(anomaly_image.cpu().numpy(), cmap='gray')  # Convert tensor to NumPy array for visualization\n",
    "    plt.show()\n",
    "else:\n",
    "    print(\"No anomalies detected.\")\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
